<?php

namespace App\Kuafu\Sms\SmsSupport;

class AliYun extends BaseSms
{
    private static $instance;
    private static $regionId = 'cn-hangzhou';
    private static $version  = '2017-05-25';

    const URL = "http://dysmsapi.aliyuncs.com/";   //url

    const COMMON_CODE_TEMPLATE = 'SMS_226945647';

    public        $url;
    private array $config;

    /**
     * get instance
     * @return AliYun
     */
    public static function getInstance()
    {
        if (!self::$instance) {
            $config         = [
                'accessKeyId'        => config('sms.ali.accessKeyId'),
                'accessKeySecret'    => config('sms.ali.accessKeySecret'),
                'signName'           => config('sms.ali.signName'),
                'commonCodeTemplate' => config('sms.ali.commonCodeTemplate')
            ];
            self::$instance = new self($config);
        }

        return self::$instance;
    }

    public function __construct($config)
    {
        $this->config = $config;
        $this->resetUrl();
    }

    /**
     * send sms
     * @param string $mobile //手机号
     * @param string $templateCode //模板
     * @param array $templateParams //模板替换参数
     * @return mixed
     * @throws \Exception
     */
    public function send(string $mobile, string $templateCode, array $templateParams)
    {
        $params = [
            'PhoneNumbers'  => $mobile,
            'SignName'      => $this->config['signName'],
            'TemplateCode'  => $templateCode,
        ];
        if ($templateParams) {
            $params['TemplateParam'] = $templateParams;
        }
        $this->arrageParam($params);
        $opts = [CURLOPT_HTTPHEADER => ["x-sdk-client" => "php/2.0.0"]];

        $result = self::request($this->url, [], $opts, true);
        $this->resetUrl();
        return $result;
    }

    /**
     * 通用验证码
     *
     * @param string $mobile
     * @param array $params
     * @param string $templateCode
     * @param string $signName
     * @return array|string
     * @throws \Exception
     */
    public function sendCommon(string $mobile, array $params, string $templateCode = '', string $signName = '')
    {
        $params = [
            'TemplateParam' => $params,
            'PhoneNumbers'  => $mobile,
            'SignName'      => $signName ?: $this->config['signName'],
            'TemplateCode'  => $templateCode ?: $this->config['commonCodeTemplate'],
        ];
        $this->arrageParam($params);
        $opts = [CURLOPT_HTTPHEADER => ["x-sdk-client" => "php/2.0.0"]];

        $result = self::request($this->url, [], $opts, true);
        $this->resetUrl();
        return $result;
    }

    /**
     * 整理参数
     */
    public function arrageParam($params)
    {

        $apiParams = array_merge(array(
            "SignatureMethod"  => "HMAC-SHA1",
            "SignatureNonce"   => uniqid(mt_rand(0, 0xffff), true),
            "SignatureVersion" => "1.0",
            "AccessKeyId"      => $this->config['accessKeyId'],
            "Timestamp"        => gmdate("Y-m-d\TH:i:s\Z"),
            "Format"           => "JSON",

        ), $params, $this->getConfig());
        if (!empty($apiParams["TemplateParam"]) && is_array($apiParams["TemplateParam"])) {
            $apiParams["TemplateParam"] = json_encode($apiParams["TemplateParam"], JSON_UNESCAPED_UNICODE);
        }
        ksort($apiParams);
        $sortedQueryStringTmp = "";
        foreach ($apiParams as $key => $value) {
            $sortedQueryStringTmp .= "&" . $this->encode($key) . "=" . $this->encode($value);
        }
        $stringToSign = "GET&%2F&" . $this->encode(substr($sortedQueryStringTmp, 1));

        $sign = base64_encode(hash_hmac("sha1", $stringToSign, $this->config['accessKeySecret'] . "&", true));

        $signature = $this->encode($sign);

        $this->url .= "?Signature={$signature}&{$sortedQueryStringTmp}";

        return $apiParams;
    }

    private function encode($str)
    {
        $res = urlencode($str);
        $res = preg_replace("/\+/", "%20", $res);
        $res = preg_replace("/\*/", "%2A", $res);
        $res = preg_replace("/%7E/", "~", $res);
        return $res;
    }

    /**
     * 基本配置
     */
    public function getConfig()
    {
        return array(
            "RegionId" => self::$regionId,
            "Action"   => "SendSms",
            "Version"  => self::$version,
        );
    }

    /**
     * 重置url
     */
    public function resetUrl()
    {
        $this->url = self::URL;
    }


    /**
     * Send a Request
     * @param string $url
     * @param array $params
     * @param int $method
     * @param array $opts array(
     *                          CURLOPT_HTTPHEADER => array(),
     *                          CURLOPT_HTTPAUTH => int,
     *                          CURLOPT_USERPWD => string,
     *                      )
     * @param boolean $returnJson
     * @return string | array
     * @throws \Exception
     */
    public static function request($url, $params = array(), $opts = array(), $returnJson = false)
    {
        $ch = curl_init();
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 5);
        curl_setopt($ch, CURLOPT_TIMEOUT, 5);

        if (strpos($url, "?") === false) {
            $url .= "?";
        } else {
            $url .= "&";
        }
        $url .= http_build_query($params);


        curl_setopt($ch, CURLOPT_URL, $url);
        if (strpos($url, "https") !== false) {
            curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
        }

        foreach ($opts as $opt => $value) {
            curl_setopt($ch, $opt, $value);
        }

        $exOutput = '';
        $result   = curl_exec($ch);
        if (curl_errno($ch) > 0) {
            $exOutput = curl_error($ch);
        }
        curl_close($ch);

        if ($returnJson) {
            $result = json_decode($result, true);
        }

        return $result;
    }
}
